Lors de la 10ème édition du Devoxx Paris, organisée au Palais des Congrès le 20 avril 2022, José Paumard et Remi Forax ont présenté une conférence sur le projet LOOM. Voici quelques liens pour pouvoir les suivre :
José Paumard : membre du Java Platform Group chez Oracle en tant que Java Developer Advocate
Remi forax : Java Champion et enseignant-chercheur spécialisé sur le langage Java
LOOM c’est quoi ?
Le projet LOOM est le prochain grand modèle de programmation concurrente sur la plateforme java. LOOM n'a pas encore vu le jour. Il est toujours en version bêta. Toutefois José Paumard et Rémi Forax confirment qu'une partie de LOOM qui concerne les threads virtuels sera enfin disponible avec le JDK 19 prévu fin 2022 (si tout se déroule bien, bien entendu 😉).
Comment fonctionne un Thread en Java ?
Un thread en Java est un wrapper d’un thread OS. Une fois qu’on lui a confié une tâche, celui-ci ne peut plus s’en détacher. Ainsi, soit il termine l'exécution de la tâche par un résultat, soit il lance une exception. Par conséquent, si notre tâche exécute des opérations d'entrée/sortie sur le réseau, elle peut être bloquée pendant un certain temps. Dans la plupart des cas, le temps d'attente est nettement plus long que le temps de traitement.
Ce que LOOM apporte
LOOM introduit un nouveau type de thread « les threads virtuels ». Nous disposons donc maintenant de deux types de threads :
Thread plateforme : il s'agit des threads système au sens de l'OS, tels que nous les connaissons aujourd’hui dans la programmation concurrente.
Thread virtuel : c'est toujours un java.lang.Thread mais pour s'exécuter, il lui faut un thread plateforme.
Un thread virtuel, pour être exécuté, a donc besoin d'un thread plateforme comme on vient de l'indiquer. Si un thread virtuel est bloqué, par exemple, par des opérations d’entrée/sortie au lieu de bloquer le thread plateforme (niveau OS), LOOM va le retirer de ce dernier et le remplacer par un autre thread virtuel.
Un thread virtuel n'est pas lié à un thread plateforme. Il peut donc se balader d'un thread Plateforme à un autre. C’est au moment où il en a besoin qu’il va avoir un thread Plateforme disponible.
En dissociant le lien fort qu’il y avait entre une tâche et un thread Plateforme, LOOM propose donc une solution pour tirer le meilleur parti des ressources système disponibles. Le but des threads virtuels est donc de gagner en latence en évitant de bloquer les threads plateformes.
À noter qu’en plus, créer des threads virtuels ne coûte pas cher !
Voici un comparatif entre thread plateforme et thread virtuel :
Thread PlateformeThread VirtuelTemps de
démarragemsµsTaille de la PileTaille fixe 2MBLa taille peut changer dynamiquementExécutionFait un appel système pour demander à l’OS de traiter le
ThreadUtilise un pool de Thread de plateforme
En théorie, on peut créer autant de thread virtuel qu’on veut beaucoup plus que ce qu’on peut créer avec les threads plateformes classiques.
Attention LOOM ne résout pas les problèmes de concurrence qu'on avait avant avec les threads classiques. Ainsi vous avez toujours besoin de mettre des « synchronized » là où il faudra les mettre.
Comment ça marche ?
La plupart du code de LOOM est écrit en java et basé sur les continuations. Dans cet article, on s’intéresse à deux méthodes de l’objet jdk. internal.vm.Continuation, yield() et run() qui permettent respectivement de copier la pile dans le tas, et de recopier du tas vers la pile ce qui a été copié au préalable.
Voilà donc, tout est une histoire de copie. Ce qui rend cela possible, ce sont les caractéristiques de la JVM et de Java. Et oui, dans java on ne manipule pas des pointeurs.😉
Attention, yield() ne marche pas si on a du code écrit en C parce qu’il est basé sur les pointeurs. En copiant les pointeurs, il n'y a aucune garantie que le code fonctionnera lorsque on appelle run() pour revenir. Dans ce cas-là, le thread virtuel ne va pas planter, mais il ne va pas non plus pouvoir être retiré du thread plateforme. Par exemple, si un thread virtuel utilise synchronized (c’est encore écrit en C dans le jdk) yield() va planter et le thread virtuel devient indissociable du thread plateforme. Il ne pourra pas être retirer et on retourne sur un modèle de thread classique, d’où l’intérêt d’utiliser les threads virtuels uniquement sur du code java.😉
Pour résumer, quand un thread virtuel rencontre un sleep ou une opération d’E/S, la méthode yield() s’exécutera pour déplacer la pile dans le tas. Une fois que l’évènement de reprise est détecté, la méthode run() remet ce qui a été copié du tas vers un thread plateforme, pas nécessairement celui d'où provient la copie.
Point de vue code
Étant donné que le projet est toujours en version bêta, vous devez télécharger un Jdk contenant LOOM. D’un point de vue code, maintenant on a deux manières de créer un thread virtuel à partir de la classe java.lang.Thread :
En utilisant la méthode static afin de créer et de démarrer un thread virtuel
2. En utilisant un builder afin de créer un thread et choisir de le démarrer avec la méthode start() ou de ne pas le démarrer avec la méthode unstarted()
Et maintenant, vous trouverez ci-dessous un exemple complet :
Conclusion
Les threads virtuels nous permettront de gagner en latence et d'exploiter les ressources du système dont nous disposons de manière plus optimale.
Référence
La vidéo de la conférence est disponible ici :
https://wiki.openjdk.org/display/loom/
https://github.com/forax/loom-fiber